package com.tjsoft.demo.util;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.impl.CriteriaImpl;
import org.hibernate.metadata.ClassMetadata;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.sun.org.apache.commons.beanutils.PropertyUtils;
/**
 * Title:泛型DAO
 * Description:该类通过泛型定义了基于实体的基础DAO。该类继承HibernateDaoSupport，提供了常用的DAO方法。
 * 基本方法只提供了简单的单条件查询，多条件的查询可以通过创建Criteria或Query来完成。建议采用Criteria构造查询，
 * 而非拼装HQL来生成Query.
 */
public class HibernateEntityDao<T> extends HibernateDaoSupport {

	private Class<T> entityClass;

	public void setEntityClass(Class<T> entityClass) {
		this.entityClass = entityClass;
	}

	/** 是否起用查询缓存 TRUE是，FALSE不启用 */
	private static final boolean ISCACHE = true;

	/**
	 * 根据指定的ID获取实体对象。
	 * @param id 实体ID
	 * @return 返回指定ID的实体对象，如果没有找到则返回null。
	 */
	@SuppressWarnings("unchecked")
	public T get(Serializable id) {
		if (id == null) {
			return null;
		}
		return (T) getHibernateTemplate().get(entityClass, id);
	}

	/**
	 * 根据指定的ID获取实体对象,支持延迟加载。
	 * @param id 实体ID
	 * @return 返回指定ID的实体对象，如果没有找到则抛出异常。
	 */
	@SuppressWarnings("unchecked")
	public T load(Serializable id) {
		if (id == null) {
			return null;
		}
		return (T) getHibernateTemplate().load(entityClass, id);
	}

	/**
	 * 保存实体对象。
	 * @param entity 待保存实体对象
	 */
	public void save(T entity) {
		getHibernateTemplate().save(entity);
	}

	/**
	 * 保存或修改持久化对象
	 * @param entity 待保存实体对象
	 */
	public void saveOrUpdate(T entity) {
		getHibernateTemplate().saveOrUpdate(entity);
	}

	/**
	 * 更新实体对象.
	 * @param entity 待更新实体对象.
	 */
	@SuppressWarnings( { "hiding", "unchecked" })
	public T merge(T entity) {
		return (T) getHibernateTemplate().merge(entity);
	}

	/**
	 * 删除实体对象。
	 * @param entity 待删除实体对象
	 */
	public void remove(T entity) {
		getHibernateTemplate().delete(entity);
	}

	/**
	 * 根据ID删除实体对象。
	 * @param id 待删除实体对象ID
	 */
	public void remove(Serializable id) {
		getHibernateTemplate().delete(get(id));
	}

	/**
	 * 删除多个实体对象
	 * @param entitys 待删除的实体对象集合
	 */
	public void remove(List<T> entitys) {
		for (T entity : entitys) {
			getHibernateTemplate().delete(entity);
		}
	}

	/**
	 * 根据属性批量删除实体对象
	 * @param name 属性名
	 * @param value 属性值
	 */
	public void removeBy(String name, Object value) {
		Query query = createQuery("delete from " + entityClass.getName() + " where " + name + "=?", value);
		query.executeUpdate();
	}

	public int remove(String hsql, Object... values) {
		int result = 0;
		Query query = createQuery(hsql, values);
		result = query.executeUpdate();
		return result;
	}

	public int removeByHql(String hsql) {
		int result = 0;
		Query query = createQuery(hsql);
		result = query.executeUpdate();
		return result;
	}

	/**
	 * 清理当前Session。
	 */
	public void clear() {
		getSession().clear();
	}

	/**
	 * 创建一个绑定实体类型的条件查询对象。
	 * @param criterions 查询条件
	 * @return 返回一个条件查询对象。
	 */
	public Criteria createCriteria(Criterion... criterions) {
		Criteria criteria = getSession().createCriteria(entityClass);
		for (Criterion c : criterions) {
			criteria.add(c);
		}
		return criteria;
	}

	/**
	 * 创建一个查询对象。
	 * @param hql HQL语句
	 * @param values 参数值
	 * @return 返回一个查询对象。
	 */
	public Query createQuery(String hql, Object... values) {
		Query query = getSession().createQuery(hql);
		for (int i = 0; i < values.length; i++) {
			query.setParameter(i, values[i]);
		}
		return query;
	}

	/**
	 * 创建一个绑定实体并设定了排序的条件查询对象。
	 * @param orderBy 排序属性
	 * @param isAsc 是否升序
	 * @param criterions 查询条件
	 * @return 返回一个已设定排序的条件查询对象。
	 */
	public Criteria createCriteria(String orderBy, Boolean isAsc, Criterion... criterions) {
		Criteria criteria = createCriteria(criterions);
		if (isAsc) {
			criteria.addOrder(Order.asc(orderBy));
		} else {
			criteria.addOrder(Order.desc(orderBy));
		}
		return criteria;
	}

	/**
	 * 获取指定类型的所有实体对象。
	 * @return 返回指定类型的所有实体对象。
	 */
	@SuppressWarnings("unchecked")
	public List<T> getAll() {
		Criteria criteria = createCriteria();
		criteria.setCacheable(ISCACHE);
		return criteria.list();
	}

	/**
	 * 获取指定类型的所有实体对象并进行排序。
	 * @param orderBy 排序的属性名
	 * @param isAsc 是否升序
	 * @return 返回排序后的指定类型的所有实体对象。
	 */
	@SuppressWarnings("unchecked")
	public List<T> getAll(String orderBy, Boolean isAsc) {
		Criteria criteria = createCriteria(orderBy, isAsc);
		criteria.setCacheable(ISCACHE);
		return criteria.list();
	}

	/**
	 * 根据属性的值查找实体对象。
	 * @param name 属性名
	 * @param value 属性值
	 * @return 返回属性值相符的实体对象集合，如果没有找到返回一个空的集合。
	 */
	@SuppressWarnings("unchecked")
	public List<T> findBy(String name, Object value) {
		Criteria criteria = createCriteria();
		if (value == null) {
			criteria.add(Restrictions.isNull(name));
		} else {
			criteria.add(Restrictions.eq(name, value));
		}
		criteria.setCacheable(ISCACHE);
		return criteria.list();
	}

	/**
	 * 根据属性的值查找实体对象并进行排序。
	 * @param name 属性名
	 * @param value 属性值
	 * @param orderBy 排序属性
	 * @param isAsc 是否升序
	 * @return 返回排序后的属性值相符的实体对象集合，如果没有找到返回一个空的集合。
	 */
	@SuppressWarnings("unchecked")
	public List<T> findBy(String name, Object value, String orderBy, boolean isAsc) {
		Criteria criteria = createCriteria(orderBy, isAsc);
		if (value == null) {
			criteria.add(Restrictions.isNull(name));
		} else {
			criteria.add(Restrictions.eq(name, value));
		}
		criteria.setCacheable(ISCACHE);
		return criteria.list();
	}

	/**
	 * 判断是否存在属性重复的实体对象。
	 * @param entity 待判断的实体对象
	 * @param propNames 属性名，可以多个属性名用","分割
	 * @return 如果存在重复的实体对象返回false，否则返回true。
	 */
	public Boolean isUnique(T entity, String propNames) {
		Criteria criteria = createCriteria().setProjection(Projections.rowCount());
		String[] nameList = propNames.split(",");
		try {
			for (String name : nameList) {
				criteria.add(Restrictions.eq(name, PropertyUtils.getProperty(entity, name)));
			}
			// 更新实体类时应该排除自身
			String idName = getIdName();
			Serializable id = getId(entity);
			if (id != null && !idName.equals(propNames)) {
				criteria.add(Restrictions.not(Restrictions.eq(idName, id)));
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return Integer.parseInt(criteria.uniqueResult().toString()) == 0;
	}

	/**
	 * 根据属性的值查找唯一的实体对象。
	 * 
	 * @param name 属性名
	 * @param value 属性值
	 * @return 返回指定唯一的实体对象，如果没有找到则返回null。
	 */
	@SuppressWarnings("unchecked")
	public T findUnique(String name, Object value) {
		Criteria criteria = createCriteria(Restrictions.eq(name, value));
		criteria.setCacheable(ISCACHE);
		return (T) criteria.uniqueResult();
	}

	/**
	 * 根据HQL查询语句进行查询。
	 * 
	 * @param hql HQL查询语句
	 * @param values 参数值
	 * @return 返回查询得到的分页对象。
	 */

	@SuppressWarnings("unchecked")
	public List<T> findList(String hql, Object... values) {
		Query query = createQuery(hql, values);
		query.setCacheable(ISCACHE);
		return query.list();
	}

	public List<T> findTopList(String hql,int top,Object... values) {
		Query query = createQuery(hql, values);
		query.setFirstResult(0);
		query.setMaxResults(top);
		query.setCacheable(ISCACHE);
		return query.list();
	}
	
	
	/**
	 * 根据HQL查询语句进行查询。 query.iterate 在启用缓存且查询数据量比较大时 性能比 query.list()高
	 * 
	 * @param hql HQL查询语句
	 * @param values 参数值
	 * @return 返回查询得到Iterator 数据集 。
	 */
	@SuppressWarnings("unchecked")
	public Iterator<T> findIterator(String hql, Object... values) {
		Query query = createQuery(hql, values);
		query.setCacheable(ISCACHE);
		return query.iterate();
	}

	/**
	 * 根据HQL查询语句进行分页查询。
	 * 
	 * @param hql HQL查询语句
	 * @param pageNo 待获取的页数
	 * @param pageSize 每页的记录数
	 * @param values 参数值
	 * @return 返回查询得到的总数。
	 */
	@SuppressWarnings("unchecked")
	public Integer getTotalCount(String hql, Object... values) {
		String countQueryString = " select count (*) " + removeSelect(removeOrders(hql));
		List countlist = createQuery(countQueryString, values).setCacheable(ISCACHE).list();
		int totalCount = Integer.parseInt(countlist.get(0).toString());

		return totalCount;
	}

	/**
	 * 根据HQL查询语句进行分页查询。
	 * 
	 * @param hql HQL查询语句
	 * @param pageNo 待获取的页数
	 * @param pageSize 每页的记录数
	 * @param values 参数值
	 * @return 返回查询得到的分页list对象。
	 */
	@SuppressWarnings("unchecked")
	public List<T> findPageList(String hql, Integer pageNo, Integer pageSize, Object... values) {
		String countQueryString = " select count (*) " + removeSelect(removeOrders(hql));
		List countlist = createQuery(countQueryString, values).setCacheable(ISCACHE).list();
		int totalCount = Integer.parseInt(countlist.get(0).toString());

		Integer pageCount = 0;
		if (totalCount % pageSize > 0) {
			pageCount = totalCount / pageSize + 1;
		} else {
			pageCount = totalCount / pageSize;
		}
		if (pageNo > pageCount) {
			pageNo = pageCount;
		}
		if (pageNo < 1) {
			pageNo = 1;
		}
		Query query = createQuery(hql, values);
		query.setCacheable(ISCACHE);
		List list = query.setFirstResult((pageNo - 1) * pageSize).setMaxResults(pageSize).list();
		return list;
	}

	/**
	 * 获取实体类的主键值。
	 */
	private Serializable getId(T entity) throws NoSuchMethodException, IllegalAccessException,
			InvocationTargetException {
		return (Serializable) PropertyUtils.getProperty(entity, getIdName());
	}

	/**
	 * 获取实体类的主键名。
	 */
	private String getIdName() {
		ClassMetadata meta = getSession().getSessionFactory().getClassMetadata(entityClass);
		return meta.getIdentifierPropertyName();
	}

	/**
	 * 去除HQL查询语句的select部分。
	 * 
	 * @param hql HQL查询语句
	 * @return 返回去除了select部分的语句。
	 */
	private String removeSelect(String hql) {
		int beginPos = hql.toLowerCase().indexOf("from");
		return hql.substring(beginPos);
	}

	/**
	 * 去除HQL查询语句的order by部分。
	 * 
	 * @param hql HQL查询语句
	 * @return 返回去除了order by部分的语句。
	 */
	private String removeOrders(String hql) {
		Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*", Pattern.CASE_INSENSITIVE);
		Matcher m = p.matcher(hql);
		StringBuffer sb = new StringBuffer();
		while (m.find()) {
			m.appendReplacement(sb, "");
		}
		m.appendTail(sb);
		return sb.toString();
	}

	/**
	 * 支持sql查询
	 * 
	 * @param sql
	 * @param values
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public List executeSQL(final String sql, final Object... values) {

		return (List) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) {
				Query query = session.createSQLQuery(sql);
				for (int i = 0; i < values.length; i++) {
					query.setParameter(i, values[i]);
				}
				return query.list();
			}

		});
	}

	/**
	 * 支持sql查询
	 * 
	 * @param sql
	 * @param values
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public List executeSQL(final String sql, final List values) {

		return (List) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) {
				Query query = session.createSQLQuery(sql);
				for (int i = 0; i < values.size(); i++) {
					query.setParameter(i, values.get(i));
				}
				return query.list();
			}

		});
	}

	/**
	 * 支持sql 新增 和修改
	 * 
	 * @param sql
	 * @param values
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public Object executeSQLInsertOrUpdate(final String sql, final Object... values) {
		return (Object) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) {
				Query query = session.createSQLQuery(sql);
				for (int i = 0; i < values.length; i++) {
					query.setParameter(i, values[i]);
				}
				return query.executeUpdate();

			}

		});
	}

	/**
	 * 支持sql 新增 和修改
	 * 
	 * @param sql
	 * @param values
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public Object executeSQLInsertOrUpdate(final String sql, final List values) {
		return (Object) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) {
				Query query = session.createSQLQuery(sql);
				for (int i = 0; i < values.size(); i++) {
					query.setParameter(i, values.get(i));
				}
				return query.executeUpdate();

			}

		});
	}

	/**
	 * 根据Criteria查询条件进行分页查询。
	 * 
	 * @param criteria criteria条件
	 * @param pageNo 待获取的页数
	 * @param pageSize 每页的记录数
	 * @return 返回查询得到的分页list对象。
	 */
	@SuppressWarnings("unchecked")
	public List<T> findPageListByCriteria(Criteria criteria, int pageNo, int pageSize) {

		CriteriaImpl impl = (CriteriaImpl) criteria;
		// 先把Projection和OrderBy条件取出来,清空两者来执行Count操作
		Projection projection = impl.getProjection();
		List<CriteriaImpl.OrderEntry> orderEntries;
		try {
			orderEntries = (List) BeanUtils.forceGetProperty(impl, "orderEntries");
			BeanUtils.forceSetProperty(impl, "orderEntries", new ArrayList());
		} catch (Exception e) {
			throw new InternalError(" Runtime Exception impossibility throw ");
		}
		// 执行查询
		int totalCount = (Integer) criteria.setProjection(Projections.rowCount()).uniqueResult();
		// 将之前的Projection和OrderBy条件重新设回去
		criteria.setProjection(projection);
		if (projection == null) {
			// 当HIBERNATE采用关联的时候如果不使用这个返回的是个对象数组，加上这个返回的是当前列表的对象。
			criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
		}
		try {
			BeanUtils.forceSetProperty(impl, "orderEntries", orderEntries);
		} catch (Exception e) {
			throw new InternalError(" Runtime Exception impossibility throw ");
		}
		Integer pageCount = 0;
		if (totalCount % pageSize > 0) {
			pageCount = totalCount / pageSize + 1;
		} else {
			pageCount = totalCount / pageSize;
		}
		if (pageNo > pageCount) {
			pageNo = pageCount;
		}
		if (pageNo < 1) {
			pageNo = 1;
		}

		List list = criteria.setFirstResult((pageNo - 1) * pageSize).setMaxResults(pageSize).list();
		return list;
	}

	/**
	 * 根据criteria查询总数。
	 * 
	 * @param criteria criteria查询条件
	 * @param pageNo 待获取的页数
	 * @param pageSize 每页的记录数
	 * @param values 参数值
	 * @return 返回查询得到的总数。
	 */
	@SuppressWarnings("unchecked")
	public Integer getTotalCountByCriteria(Criteria criteria) {
		CriteriaImpl impl = (CriteriaImpl) criteria;
		try {
			// 清空Projection和OrderBy两者来执行Count操作
			BeanUtils.forceSetProperty(impl, "orderEntries", new ArrayList());
		} catch (Exception e) {
			throw new InternalError(" Runtime Exception impossibility throw ");
		}
		// 执行查询
		int totalCount = (Integer) criteria.setProjection(Projections.rowCount()).uniqueResult();

		return totalCount;
	}

	/**
	 * 支持 HQL方式 更新数据
	 * 
	 * @param hql
	 * @param values
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public Object update(final String hql, final Object... values) {

		return (Object) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) {
				Query query = session.createQuery(hql);
				for (int i = 0; i < values.length; i++) {
					query.setParameter(i, values[i]);
				}
				return query.executeUpdate();
			}

		});
	}

	/***************************************************************************
	 * HQL查询结果集 根据结果集 返回结果集记录总数
	 * 
	 * @param countQueryString
	 * @param values
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public Integer getQueryPageListCount(String hql, Object... values) {
		int totalCount = 0;
		List countlist = createQuery(hql, values).setCacheable(ISCACHE).list();
		if (countlist != null) {
			totalCount = countlist.size();
		}
		return totalCount;
	}

	/***************************************************************************
	 * 分页查询查询记录 同 getQueryPageListCount(String hql,Object...values) 一起使用 达到分页查询
	 * 
	 * @param hql
	 * @param pageNo 当前页
	 * @param pageSize 每页大小
	 * @param values
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public List<T> findQueryPageList(String hql, Integer pageNo, Integer pageSize, Object... values) {
		Query query = createQuery(hql, values);
		query.setCacheable(ISCACHE);
		List list = query.setFirstResult((pageNo - 1) * pageSize).setMaxResults(pageSize).list();
		return list;
	}

	/**
	 * 根据HQL查询语句进行分页查询。
	 * 
	 * @param hql HQL查询语句
	 * @param pageNo 待获取的页数
	 * @param pageSize 每页的记录数
	 * @param values 参数值
	 * @return 返回查询得到的分页对象。
	 */
	@SuppressWarnings("unchecked")
	public Page<T> findPage(String hql, Integer pageNo, Integer pageSize, Object... values) {
		String countQueryString = " select count (*) " + removeSelect(removeOrders(hql));
		List countlist = createQuery(countQueryString, values).setCacheable(ISCACHE).list();
		int totalCount = Integer.parseInt(countlist.get(0).toString());

		if (totalCount < 1) {
			return new Page<T>();
		}
		Integer pageCount = 0;
		if (totalCount % pageSize > 0) {
			pageCount = totalCount / pageSize + 1;
		} else {
			pageCount = totalCount / pageSize;
		}
		if (pageNo > pageCount) {
			pageNo = pageCount;
		}
		Query query = createQuery(hql, values);
		query.setCacheable(ISCACHE);
		List list = query.setFirstResult((pageNo - 1) * pageSize).setMaxResults(pageSize).list();

		Page<T> page = new Page<T>(totalCount, pageNo, pageSize);
		page.setContents(list);
		return page;
	}

	/**
	 * 原生SQL 分页查询
	 * 
	 * @param sql
	 * @param pageNo 待获取的页数
	 * @param pageSize 每页的记录数
	 * @param values 参数值
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public Page<T> findPageBySQL(final String sql, final Integer pageNo, final Integer pageSize, final Object... values) {

		return (Page<T>) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) {

				String countQueryString = " select count (*) " + removeSelect(removeOrders(sql));
				Query countQuery = session.createSQLQuery(countQueryString);
				for (int i = 0; i < values.length; i++) {
					countQuery.setParameter(i, values[i]);
				}
				List countlist = countQuery.list();

				int totalCount = Integer.parseInt(countlist.get(0).toString());

				if (totalCount < 1) {
					return new Page<T>();
				}
				Integer pageCount = 0;
				if (totalCount % pageSize > 0) {
					pageCount = totalCount / pageSize + 1;
				} else {
					pageCount = totalCount / pageSize;
				}
				int no = pageNo;
				if (no > pageCount) {
					no = pageCount;
				}

				Query query = session.createSQLQuery(sql);
				for (int i = 0; i < values.length; i++) {
					query.setParameter(i, values[i]);
				}
				List list = query.setFirstResult((no - 1) * pageSize).setMaxResults(pageSize).list();

				Page<T> page = new Page<T>(totalCount, no, pageSize);
				page.setContents(list);
				return page;
			}

		});
	}
}
